/*
 * Copyright 2007 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.zxing.qrcode;

import java.util.List;
import java.util.Map;

import android.app.Activity;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.ContexWiFiManager;
import com.google.zxing.client.android.LocatioManager;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.qrcode.decoder.Decoder;
import com.google.zxing.qrcode.detector.Detector;

/**
 * This implementation can detect and decode QR Codes in an image.
 * 
 * @author Sean Owen
 */
public class QRCodeReader implements Reader {

	private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
	private Activity activ;

	public QRCodeReader(Activity activ) {
		this.activ = activ;
	}

	public QRCodeReader() {
		super();
	}

	private final Decoder decoder = new Decoder();

	protected Decoder getDecoder() {
		return decoder;
	}

	/**
	 * Locates and decodes a QR code in an image.
	 * 
	 * @return a String representing the content encoded by the QR code
	 * @throws NotFoundException
	 *             if a QR code cannot be found
	 * @throws FormatException
	 *             if a QR code cannot be decoded
	 * @throws ChecksumException
	 *             if error correction fails
	 */
	@Override
	public Result decode(BinaryBitmap image) throws NotFoundException,
			ChecksumException, FormatException {
		return decode(image, null);
	}

	@Override
	public Result decode(BinaryBitmap image, Map<DecodeHintType, ?> hints)
			throws NotFoundException, ChecksumException, FormatException {
		DecoderResult decoderResult;
		ResultPoint[] points;
		if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
			BitMatrix bits = extractPureBits(image.getBlackMatrix());
			decoderResult = decoder.decode(bits, hints);
			points = NO_POINTS;
		} else {
			DetectorResult detectorResult = new Detector(image.getBlackMatrix())
					.detect(hints);
			decoderResult = decoder.decode(detectorResult.getBits(), hints);
			points = detectorResult.getPoints();
		}

		// Old code

		// String wifiPart = "";
		// Context c = getActiv().getApplicationContext();
		// WifiManager wifi = (WifiManager)
		// c.getSystemService(Context.WIFI_SERVICE);
		// if(wifi.isWifiEnabled()){
		// List<ScanResult> wifiScanResult = wifi.getScanResults();
		// if(wifiScanResult!= null && !wifiScanResult.isEmpty()){
		// int count = 0;
		// for(ScanResult r:wifiScanResult){
		// if(count<3){
		// wifiPart+="?ssid="+r.SSID+"?mac="+r.BSSID+"?rssi="+r.level;
		// count++;
		//
		// }else {
		// break;
		// }
		// }
		// }
		// }
		// LocationManager loc = (LocationManager)
		// c.getSystemService(Context.LOCATION_SERVICE);
		// Criteria cr = new Criteria();
		// String provider = loc.getBestProvider(cr, true);
		// loc.requestLocationUpdates(provider, 2000, 1, this);
		// Location best;
		//
		// if(provider.equalsIgnoreCase(LocationManager.GPS_PROVIDER)){
		//
		// best = loc.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		//
		// }else if (provider.equalsIgnoreCase(LocationManager.GPS_PROVIDER)) {
		//
		// best = loc.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
		//
		// }else {
		//
		// best = loc.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
		// }
		//
		//
		//
		//
		// String lalitude = ""+best.getLatitude();
		// String longitude = ""+best.getLongitude();
		//
		// String coordinat = "";
		// if(lalitude!= null && longitude!= null && lalitude.length()>0 &&
		// longitude.length()>0){
		// coordinat+="?lat="+lalitude+"?lng"+longitude;
		// }

		String coordinat = "";
		String wifiContexAware = "";
		if (decoderResult.getText().toLowerCase().contains("http:")) {
			LocatioManager locatio = new LocatioManager(getActiv());
			locatio.checkLocation();
			coordinat = locatio.buildResult();

			ContexWiFiManager wifiContext = new ContexWiFiManager(getActiv());
			wifiContext.checkLocationContext();
			wifiContexAware = wifiContext.buildResultWifiContext();

		}
		Result result = new Result(decoderResult.getText() + coordinat
				+ wifiContexAware, decoderResult.getRawBytes(), points,
				BarcodeFormat.QR_CODE);

		List<byte[]> byteSegments = decoderResult.getByteSegments();
		if (byteSegments != null) {
			result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
		}
		String ecLevel = decoderResult.getECLevel();
		if (ecLevel != null) {
			result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL,
					ecLevel);
		}
		return result;
	}

	@Override
	public void reset() {
		// do nothing
	}

	/**
	 * This method detects a code in a "pure" image -- that is, pure monochrome
	 * image which contains only an unrotated, unskewed, image of a code, with
	 * some white border around it. This is a specialized method that works
	 * exceptionally fast in this special case.
	 * 
	 * @see com.google.zxing.pdf417.PDF417Reader#extractPureBits(BitMatrix)
	 * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
	 */
	private static BitMatrix extractPureBits(BitMatrix image)
			throws NotFoundException {

		int[] leftTopBlack = image.getTopLeftOnBit();
		int[] rightBottomBlack = image.getBottomRightOnBit();
		if (leftTopBlack == null || rightBottomBlack == null) {
			throw NotFoundException.getNotFoundInstance();
		}

		float moduleSize = moduleSize(leftTopBlack, image);

		int top = leftTopBlack[1];
		int bottom = rightBottomBlack[1];
		int left = leftTopBlack[0];
		int right = rightBottomBlack[0];

		if (bottom - top != right - left) {
			// Special case, where bottom-right module wasn't black so we found
			// something else in the last row
			// Assume it's a square, so use height as the width
			right = left + (bottom - top);
		}

		int matrixWidth = Math.round((right - left + 1) / moduleSize);
		int matrixHeight = Math.round((bottom - top + 1) / moduleSize);
		if (matrixWidth <= 0 || matrixHeight <= 0) {
			throw NotFoundException.getNotFoundInstance();
		}
		if (matrixHeight != matrixWidth) {
			// Only possibly decode square regions
			throw NotFoundException.getNotFoundInstance();
		}

		// Push in the "border" by half the module width so that we start
		// sampling in the middle of the module. Just in case the image is a
		// little off, this will help recover.
		int nudge = (int) (moduleSize / 2.0f);
		top += nudge;
		left += nudge;

		// Now just read off the bits
		BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
		for (int y = 0; y < matrixHeight; y++) {
			int iOffset = top + (int) (y * moduleSize);
			for (int x = 0; x < matrixWidth; x++) {
				if (image.get(left + (int) (x * moduleSize), iOffset)) {
					bits.set(x, y);
				}
			}
		}
		return bits;
	}

	private static float moduleSize(int[] leftTopBlack, BitMatrix image)
			throws NotFoundException {
		int height = image.getHeight();
		int width = image.getWidth();
		int x = leftTopBlack[0];
		int y = leftTopBlack[1];
		boolean inBlack = true;
		int transitions = 0;
		while (x < width && y < height) {
			if (inBlack != image.get(x, y)) {
				if (++transitions == 5) {
					break;
				}
				inBlack = !inBlack;
			}
			x++;
			y++;
		}
		if (x == width || y == height) {
			throw NotFoundException.getNotFoundInstance();
		}
		return (x - leftTopBlack[0]) / 7.0f;
	}

	public Activity getActiv() {
		return activ;
	}

	public void setActiv(Activity activ) {
		this.activ = activ;
	}

}
